/*
 * Licensed to the 南京汇海交通科技有限公司
 *
 * Copyright (c) 2025.
 * All rights reserved.
 *
 * 本软件属南京汇海交通科技有限公司所有，在未获得南京汇海交通科技有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.chframework.common.extend;

import cn.hutool.json.JSONUtil;
import com.chframework.common.exception.ApplicationException;
import com.chframework.common.exception.ServiceException;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletionException;
import java.util.function.Supplier;

public class ExceptionHelper {

    /**
     * 打印详细错误信息
     */
    public static String printException(Throwable ex) {
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);
        ex.printStackTrace(printWriter);
        printWriter.flush();
        printWriter.close();
        return stringWriter.toString();
    }

    /**
     * 解析异常消息
     * <p>
     * 首先，需要明确两点：
     * （1）系统中目前主动抛出的异常类型只有两种：SystemInterrupterException 和 SystemMessageException
     * （2）为什么会有这个方法？
     * - 场景：在java并发编程中，当使用CompletableFuture工具对多线程任务进行编排时抛出的检查异常和未检查异常会被统一包装为CompletionException；
     * - 变化：自定义异常消息会被加上异常类型的package前缀；
     * - 解析：移除异常消息前缀。
     */
    public static String getMessage(Exception exception) {
        String message = exception.getMessage();
        // 如果是表示接口响应状态的json报文，just skip it.
        if (JSONUtil.isTypeJSONObject(message)) {
            return message;
        }

        Supplier<Boolean> hasBeenWrapper = () -> {
            // 全局统一异常处理程序只处理这三类异常，未被捕获的异常则按照系统异常进行处理！
            return ServiceException.class.isAssignableFrom(exception.getClass())
                    || ApplicationException.class.isAssignableFrom(exception.getClass())
                    || CompletionException.class.isAssignableFrom(exception.getClass());
        };

        // 如果捕获到的异常类型是CompletionException、ServiceException、ApplicationException或者是它们的派生类，just do it.
        if (hasBeenWrapper.get()) {
            // 微服务层统一对外抛出的异常类型
            message = StringHelper.trimStart(message, ServiceException.class.getName() + ":").trim();
            // 请求转发层统一对外抛出的异常类型
            message = StringHelper.trimStart(message, ApplicationException.class.getName() + ":").trim();
        }

        return message;
    }

    /**
     * 追溯堆栈信息，判断是否为预期中的异常消息。
     * 也就是判断是不是程序员主动抛出的自定义异常消息！
     * 方式1
     */
    public static boolean canTraceTo(Throwable throwable, Class<?> clazz) {
        if (throwable == null) {
            return false;
        }
        if (clazz.equals(throwable.getClass())) {
            return true;
        }
        return canTraceTo(throwable.getCause(), clazz);
    }

    /**
     * 获取异常的堆栈类型列表，可用于判断是否为预期中的异常消息
     * 也就是判断是不是程序员主动抛出的自定义异常消息！
     * 方式2
     */
    public static List<Class<?>> getCauseList(Throwable throwable) {
        List<Class<?>> causeList = new ArrayList<>();
        collectCause(causeList, throwable);
        return causeList;
    }

    /**
     * 尾递归方式获取异常的堆栈类型列表
     */
    private static void collectCause(List<Class<?>> causeList, Throwable throwable) {
        if (throwable == null) {
            return;
        }
        causeList.add(throwable.getClass());
        collectCause(causeList, throwable.getCause());
    }

}
